package com.biz.primus.ms.payment.service;

import com.biz.primus.base.exception.BizBusinessException;
import com.biz.primus.common.utils.CopyUtils;
import com.biz.primus.common.utils.JsonUtils;
import com.biz.primus.model.payment.enums.PaymentType;
import com.biz.primus.model.payment.vo.req.SupportPaymentSyncReqVo;
import com.biz.primus.model.payment.vo.resp.SupPaymentRespVo;
import com.biz.primus.ms.base.service.AbstractBaseService;
import com.biz.primus.ms.payment.dao.po.SupportPaymentLog;
import com.biz.primus.ms.payment.dao.po.SupportPaymentPo;
import com.biz.primus.ms.payment.dao.repository.SupportPaymentLogRepository;
import com.biz.primus.ms.payment.dao.repository.SupportPaymentRepository;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StopWatch;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * SupportPaymentService
 *
 * @author JKLiues
 * @date 2017年11月16日
 */
@Service
@Slf4j
public class SupportPaymentService extends AbstractBaseService {

    private static final String LIST_KEY = "listKey";
    private final SupportPaymentLogRepository supportPaymentLogRepository;
    private final SupportPaymentRepository supportPaymentRepository;

    @Autowired
    public SupportPaymentService(SupportPaymentRepository supportPaymentRepository, SupportPaymentLogRepository supportPaymentLogRepository) {
        this.supportPaymentRepository = supportPaymentRepository;
        this.supportPaymentLogRepository = supportPaymentLogRepository;
    }


    @Transactional(rollbackFor = Exception.class)
    public void syncSupportPayment(List<SupportPaymentSyncReqVo> reqVos) {
        StopWatch stopWatch = new StopWatch("同步支付方式方法");
        stopWatch.start("进行同步支付方式");
        List<SupportPaymentPo> supportPaymentPos = this.buildSupportPayments(reqVos);
        supportPaymentRepository.save(supportPaymentPos);
        stopWatch.stop();
        stopWatch.start("记录同步日志");
        this.log(reqVos);
        stopWatch.stop();
        stopWatch.prettyPrint();
        delayer.executeAfterTransactionCommit(() -> supportPaymentPos.forEach(po -> supportPaymentPoCacheLoader.refresh(po.getCode())));
    }

    public SupPaymentRespVo findByCode(String code) {
        try {
            SupportPaymentPo supportPaymentPo = supportPaymentPoCacheLoader.get(code);
            SupPaymentRespVo vo = new SupPaymentRespVo();
            CopyUtils.copyProperties(supportPaymentPo, vo);
            return vo;
        } catch (ExecutionException e) {
            log.error("找不到code为{}的支付方式");
            return null;
        }
    }

    public List<SupPaymentRespVo> findAvailable() {
        try {
            List<SupportPaymentPo> supportPaymentPos = supportPaymentListCacheLoader.get(LIST_KEY);
            return Optional.ofNullable(supportPaymentPos).orElse(Lists.newArrayList())
                    .stream()
                    .map(po -> {
                        SupPaymentRespVo vo = new SupPaymentRespVo();
                        CopyUtils.copyProperties(po, vo);
                        return vo;
                    }).collect(Collectors.toList());
        } catch (ExecutionException e) {
            e.printStackTrace();
            return Lists.newArrayList();
        }
    }

    @Transactional(rollbackFor = Exception.class)
    public void updateRelation(Long id, PaymentType paymentType) {
        SupportPaymentPo one = supportPaymentRepository.findOne(id);
        if (one == null) {
            throw new BizBusinessException("支付方式不存在");
        }
        one.setSysType(paymentType);
        supportPaymentRepository.save(one);
        delayer.executeAfterTransactionCommit(() -> {
            supportPaymentPoCacheLoader.refresh(one.getCode());
        });
    }

    private List<SupportPaymentPo> buildSupportPayments(List<SupportPaymentSyncReqVo> reqVos) {
        if (CollectionUtils.isEmpty(reqVos)) {
            return Lists.newArrayList();
        }
        List<String> codes = reqVos.stream().map(SupportPaymentSyncReqVo::getCode).collect(Collectors.toList());
        log.info("------------获取数据库中的现有支付方式:{}", codes.toString());
        List<SupportPaymentPo> supPos = supportPaymentRepository.findByCodeIn(codes);
        log.info("------------数据库中的支付方式{}", supPos.toString());
        Map<String, SupportPaymentPo> map = Optional.of(supPos).orElse(new ArrayList<>()).stream()
                .collect(Collectors.toMap(SupportPaymentPo::getCode, po -> po));
        return reqVos.stream().map(item -> {
            String code = item.getCode();
            SupportPaymentPo supportPaymentPo = map.get(code);
            if (supportPaymentPo == null) {
                supportPaymentPo = new SupportPaymentPo();
                supportPaymentPo.setId(idService.getNextId());
            }
            CopyUtils.copyProperties(item, supportPaymentPo);
            return supportPaymentPo;
        }).collect(Collectors.toList());
    }

    private LoadingCache<String, SupportPaymentPo> supportPaymentPoCacheLoader = CacheBuilder
            .newBuilder()
            .expireAfterWrite(1, TimeUnit.HOURS)
            .build(new CacheLoader<String, SupportPaymentPo>() {
                @Override
                public SupportPaymentPo load(String key) throws Exception {
                    return supportPaymentRepository.findByCode(key);
                }
            });

    private LoadingCache<String, List<SupportPaymentPo>> supportPaymentListCacheLoader = CacheBuilder
            .newBuilder()
            .expireAfterWrite(1, TimeUnit.HOURS)
            .build(new CacheLoader<String, List<SupportPaymentPo>>() {
                @Override
                public List<SupportPaymentPo> load(String key) throws Exception {
                    return supportPaymentRepository.findAll();
                }
            });

    private void log(List<SupportPaymentSyncReqVo> reqVos) {
        List<SupportPaymentLog> collect = Optional.ofNullable(reqVos).orElse(new ArrayList<>())
                .stream().map(vo -> {
                    SupportPaymentLog log = new SupportPaymentLog();
                    log.setCode(vo.getCode());
                    log.setId(idService.getNextId());
                    log.setJsonStr(JsonUtils.obj2Json(vo));
                    return log;
                }).collect(Collectors.toList());
        if (CollectionUtils.isNotEmpty(collect)) {
            supportPaymentLogRepository.save(collect);
        }
    }
}